-- Project: s32k148 (ported from S32 Design Studio)
-- Toolchain: arm-none-eabi-gcc/newlib (hard-float, Cortex-M4, Thumb)

set_project("s32k148")
set_version("1.0.0")

-- Build modes
add_rules("mode.debug", "mode.release")
set_languages("c11")
set_defaultmode("debug")
-- set_defaultmode("release")
set_defaultplat("cross")
set_defaultarchs("arm")

-- Define standalone arm-none-eabi toolchain (auto-detected if in PATH)
toolchain("arm-none-eabi")
    set_kind("standalone")
    set_toolset("cc",  "arm-none-eabi-gcc")
    set_toolset("cxx", "arm-none-eabi-g++")
    set_toolset("as",  "arm-none-eabi-gcc")
    set_toolset("ar",  "arm-none-eabi-ar")
    set_toolset("ld",  "arm-none-eabi-gcc")
    set_toolset("sh",  "arm-none-eabi-g++")
    set_toolset("ex",  "arm-none-eabi-objcopy")
    set_toolset("mm",  "arm-none-eabi-objdump")
    set_toolset("gdb", "arm-none-eabi-gdb")
toolchain_end()

set_toolchains("arm-none-eabi")

-- Option: choose flash or ram linker script
option("link")
    set_default("flash")
    set_showmenu(true)
    set_description("Select linker script: flash | ram")
option_end()

local link = get_config("link") or "flash"
local link_script = (link == "ram") and "LinkFile/linker_ram_s32k148.ld" or "LinkFile/linker_flash_s32k148.ld"
local start_define = (link == "ram") and "START_FROM_RAM" or "START_FROM_FLASH"

-- MCU common flags
local mcu_cflags = {
    "-mcpu=cortex-m4",
    "-mthumb",
    "-mfloat-abi=hard",
    "-mfpu=fpv4-sp-d16",
    "-ffunction-sections",
    "-fdata-sections"
}
local mcu_asflags = {
    "-mcpu=cortex-m4",
    "-mthumb",
    "-mfloat-abi=hard",
    "-mfpu=fpv4-sp-d16"
}
local mcu_ldflags = {
    "-mcpu=cortex-m4",
    "-mthumb",
    "-mfloat-abi=hard",
    "-mfpu=fpv4-sp-d16",
    "-Wl,--gc-sections"
}

-- Target
local outname = "s32k148"

-- Note: xmake sandbox restricts Lua stdlib (io/pcall/import). For map summary,
-- we invoke an external Python helper to parse and print results.

-- Custom task: xmake mapreport (print latest map summary)
task("mapreport")
    set_menu({
        usage = "xmake mapreport",
        description = "Print Flash/RAM/Stack usage summary from .map",
        options = {}
    })
    on_run(function()
        local pattern = path.join("build", "**", outname .. ".map")
        local files = os.files(pattern) or {}
        if #files == 0 then
            cprint("${yellow}mapreport: no map file found under build/**")
            return
        end
        -- Pick the most recently modified
        local latest = files[1]
        local latest_m = os.mtime(latest) or 0
        for _, fpath in ipairs(files) do
            local m = os.mtime(fpath) or 0
            if m > latest_m then latest, latest_m = fpath, m end
        end
        -- Run external helper; don't fail if python3 is missing
        local cmd = string.format("python3 tools/mapreport.py '%s' || true", latest)
        os.vrunv("bash", {"-lc", cmd})
    end)
task_end()

target("s32k148_build")
    set_kind("binary")
    -- Fixed output directories per mode, independent of global plat/arch
    set_targetdir("build/$(mode)")
    set_objectdir("build/$(mode)/.objs")
    set_dependir("build/$(mode)/.deps")
    set_filename(outname .. ".elf")

    -- Defines (from Debug_FLASH)
    add_defines(
        "COMPILER_GCC",
        "DISABLE_MCAL_INTERMODULE_ASR_CHECK",
        "S32K148",
        "USE_SW_VECTOR_MODE",
        "I_CACHE_ENABLE",
        "ENABLE_FPU"
    )

    -- Include directories (from Debug_FLASH)
    add_includedirs(
        "ASW",
        "BSW/CommonInclude",
        "BSW/Communication/CAN/CanIf",
        "BSW/Communication/CAN/CanNm",
        "BSW/Communication/CAN/CanSM",
        "BSW/Communication/CAN/CanTp",
        "BSW/Communication/Common/Com",
        "BSW/Communication/Common/Dcm",
        "BSW/Communication/Common/Dcm/UDS",
        "BSW/Communication/Common/NmIf",
        "BSW/Communication/Common/PDUR",
        "BSW/Config/BSW_Config",
        "BSW/Config/BSW_Config/RTE",
        "BSW/Config/BSW_Config/RTE/SchM",
        "BSW/Config/MCAL_Config",
        "BSW/Config/MCAL_Config/include",
        "BSW/Libraries/Crc",
        "BSW/Libraries/E2E",
        "BSW/Libraries/IStd",
        "BSW/MCAL/BaseNXP_TS_T40D2M20I0R0/header",
        "BSW/MCAL/BaseNXP_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Can_43_FLEXCAN_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Dio_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Fee_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Gpt_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Mcu_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Mem_43_INFLS_TS_T40D2M20I0R0/include",
        "BSW/MCAL/MemAcc_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Os_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Platform_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Platform_TS_T40D2M20I0R0/startup/include",
        "BSW/MCAL/Port_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Rte_TS_T40D2M20I0R0/include",
        "BSW/MCAL/Wdg_TS_T40D2M20I0R0/include",
        "BSW/Memory/MemIf",
        "BSW/Memory/NvM",
        "BSW/SystemServices/BswM",
        "BSW/SystemServices/ComM",
        "BSW/SystemServices/Dem",
        "BSW/SystemServices/Det",
        "BSW/SystemServices/EcuM",
        "BSW/SystemServices/OS/Extend",
        "BSW/SystemServices/OS/Kernel",
        "BSW/SystemServices/OS/Portable/Mcu/NXP/S32K1XX",
        "BSW/SystemServices/OS/Portable/Processor/ARMV7/CortexM",
        "BSW/SystemServices/WdgIf",
        "BSW/SystemServices/WdgM"
    )

    add_files(
        "ASW/*.c",
        "BSW/CommonInclude/**.c",
        "BSW/Communication/**.c",
        "BSW/Config/**.c",
        "BSW/Libraries/**.c",
        "BSW/MCAL/**.c",
        "BSW/MCAL/**.s",
        "BSW/Memory/**.c",
        "BSW/SystemServices/**.c",
        "BSW/SystemServices/**.s"
    )

    remove_files(
        "BSW/MCAL/Dio_TS_T40D2M20I0R0/**"
    )

    -- Flags
    add_cflags(mcu_cflags, {force = true})
    add_asflags(mcu_asflags, {force = true})

    -- Ensure .s assembly files are preprocessed to expand macros
    add_asflags("-x", "assembler-with-cpp", {force = true})
    add_asflags("-D" .. start_define)
    add_asflags("-DCOMPILER_GCC")

    if is_mode("debug") then
        add_defines("DEBUG")
        add_cflags("-g3")
        add_asflags("-g3")
    else
        add_cflags("-O2")
    end

    -- Linker
    add_ldflags(mcu_ldflags, {force = true})
    -- Suppress ld warning: LOAD segment with RWX permissions
    add_ldflags("-Wl,--no-warn-rwx-segments", {force = true})
    add_ldflags("-nostartfiles", {force = true})
    add_ldflags("-T" .. link_script, {force = true})
    -- Generate map file into fixed build directory per mode
    add_ldflags("-Wl,-Map=build/$(mode)/" .. outname .. ".map", {force = true})



    -- Post-build: hex/bin/srec and size
    after_build(function (target)
        local objcopy = "arm-none-eabi-objcopy"
        local size = "arm-none-eabi-size"
        local elf = target:targetfile()
        local outbase = path.join(path.directory(elf), outname)
        -- Generate Intel HEX and SREC (carry addresses, safe for flashing)
        os.vrunv(objcopy, {"-O", "ihex", elf, outbase .. ".hex"})
        os.vrunv(objcopy, {"-O", "srec", elf, outbase .. ".srec"})

        -- Generate compact BIN only from flash-resident sections to avoid
        -- huge sparse files caused by RAM sections at 0x200xxxxx.
        -- Include sections actually placed in on-chip flash by the linker
        -- (per linker_flash_s32k148.ld these are consolidated under .flash*).
        local bin_sections = {
            ".flash", ".flash_config", ".flash_interrupts",
            -- Fallback common names used by some linkers/startups
            ".isr_vector", ".vectors", ".interrupts",
            ".text", ".rodata", ".data",
            ".init", ".fini", ".ctors", ".dtors"
        }
        local bin_args = {"-O", "binary"}
        for _, sec in ipairs(bin_sections) do
            table.insert(bin_args, "-j")
            table.insert(bin_args, sec)
        end
        table.insert(bin_args, elf)
        table.insert(bin_args, outbase .. ".bin")
        os.vrunv(objcopy, bin_args)

        os.vrunv(size, {"-A", elf})

        -- Print memory usage summary from map via external helper (best-effort)
        local cmd = string.format("python3 tools/mapreport.py '%s' || true", outbase .. ".map")
        os.vrunv("bash", {"-lc", cmd})
    end)
